<?php

namespace Orqlog\Yacampaign\Domain\Service;

use Orqlog\Yacampaign\Domain\Model\AddressInterface;
use Orqlog\Yacampaign\Domain\Repository\AddressRepositoryInterface;
use Orqlog\Yacampaign\Domain\Repository\CampaignRepositoryInterface;
use Orqlog\Yacampaign\Domain\Repository\JoinRecordRepositoryInterface;
use Orqlog\Yacampaign\Domain\Repository\UserRepositoryInterface;
use Orqlog\Yacampaign\Exception\IllegalArgumentException;
use Orqlog\Yacampaign\Exception\LackDependencyException;

class UserService implements UserServiceInterface
{

    /**
     * @var \Orqlog\Yacampaign\Domain\Repository\JoinRecordRepositoryInterface
     */
    protected $joinRecordRepository;

    /**
     * @var \Orqlog\Yacampaign\Domain\Repository\CampaignRepositoryInterface
     */
    protected $campaignRepository;

    /**
     * @var \Orqlog\Yacampaign\Domain\Repository\UserRepositoryInterface
     */
    protected $userRepository;

    /**
     * @var \Orqlog\Yacampaign\Domain\Repository\AddressRepositoryInterface
     */
    protected $addressRepository;

    /**
     * @var \Orqlog\Yacampaign\Domain\Service\CampaignServiceInterface
     */
    protected $campaignService;


    /**
     * Inject JoinRecordRepository
     */
    public function setJoinRecordRepository(JoinRecordRepositoryInterface $joinRecordRepository) :void 
    {
        $this->joinRecordRepository = $joinRecordRepository;
    }

     /**
     * Inject CampaignRepository
     */
    public function setCampaignRepository(CampaignRepositoryInterface $campaignRepository) :void 
    {
        $this->campaignRepository = $campaignRepository;
    }

    /**
     * Inject UserRepository
     */
    public function setUserRepository(UserRepositoryInterface $userRepository) :void 
    {
        $this->userRepository = $userRepository;
    }

    /**
     * Inject AddressRepository
     */
    public function setAddressRepository(AddressRepositoryInterface $addressRepository) :void 
    {
        $this->addressRepository = $addressRepository;
    }

    /**
     * Inject CampaignService
     */
    public function setCampaignService(CampaignServiceInterface $campaignService) :void 
    {
        $this->campaignService = $campaignService;
    }


    public function getAddressesForUser(int $userId):array
    {

        if (!$this->addressRepository instanceof AddressRepositoryInterface) {
            throw new LackDependencyException("Please provide AddressRepositoryInterface implementaion", 1592812464);
        }

        return $this->addressRepository->getAddressesForUser($userId);
    }

    public function addAddress(int $userId, array $addressData):void  
    {
        if (!isset($addressData['id']) && !$this->addressRepository instanceof AddressRepositoryInterface) {
            throw new LackDependencyException("Please provide AddressRepositoryInterface implementaion", 1592811853);
        }

        if (!$this->userRepository instanceof UserRepositoryInterface) {
            throw new LackDependencyException("Please provide UserRepositoryInterface implementaion", 1592811893);
        }

        if (!isset($addressData['id'])) {
            $addressId = $this->addressRepository->createAddress($addressData);
        } else {
            $addressId = $addressData['id'];
        }
        $this->userRepository->attachAddress($userId, $addressId);
    }

    public function removeAddress(int $userId, int $addressId):void 
    {
        if (!$this->userRepository instanceof UserRepositoryInterface) {
            throw new LackDependencyException("Please provide UserRepositoryInterface implementaion", 1592812095);
        }

        $this->userRepository->detachAddress($userId, $addressId);
    }
    
    public function getDefaultAddressForUser(int $userId):?AddressInterface 
    {
        $addresses = $this->getAddressesForUser($userId);
        foreach ($addresses as $address) {
            if ($address->isDefault()) {
                return $address;
            }
        }
        return null;
    }

    public function getJoinRecords(int $campaignId, int $userId):array 
    {
        
        if (!isset($this->joinRecordRepository) || ! $this->joinRecordRepository instanceof JoinRecordRepositoryInterface) {
            throw new LackDependencyException("Please provide implementation for JoinRecordRepositoryInterface.", 1592808389);
        }

        return $this->joinRecordRepository->getJoinRecords($campaignId, $userId);
    }


    /**
     * User join the campaign, if user is qualified to join it will make a JoinRecord instance
     * 
     * The subclass may extends this method and persist JoinRecord
     */
    public function joinCampaign(int $campaignId, int $userId) :void
    {
        if (!isset($this->campaignRepository) || ! $this->campaignRepository instanceof CampaignRepositoryInterface) {
            throw new LackDependencyException("Please provide implementation for CampaignRepositoryInterface.", 1592808132);
        }

        if (!isset($this->campaignService) || ! $this->campaignService instanceof CampaignServiceInterface) {
            throw new LackDependencyException("Please provide implementation for CampaignServiceInterface.", 1592808297);
        }

        if (!isset($this->joinRecordRepository) || ! $this->joinRecordRepository instanceof JoinRecordRepositoryInterface) {
            throw new LackDependencyException("Please provide implementation for JoinRecordRepositoryInterface.", 1592808389);
        }

        $campaign = $this->campaignRepository->findById($campaignId);
        if (!$campaign->isOpen()) {
            throw new IllegalArgumentException('The campaign is closed!', '1592805988');
        }

        if (!$this->campaignService->isQualified($userId, $campaignId)) {
            throw new IllegalArgumentException('The user is not qualified to join!', '1592806175');
        }

        $data = [
            'joined_at' => new \DateTime(),
            'campaign_id' => $campaignId,
            'user_id' => $userId,
        ];

        $this->joinRecordRepository->createJoinRecord($data);
    }

}